package com.aiwiown.snackmq.storage.util;

import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;

/**
 * 测试资源清理工具类
 * 用于在测试结束后强制清理所有资源，避免文件句柄泄漏
 */
@Slf4j
public final class TestResourceCleaner {

    private TestResourceCleaner() {}

    /**
     * 强制清理测试目录
     * 
     * @param testDir 测试目录路径
     * @param maxRetries 最大重试次数
     * @param retryDelayMs 重试延迟（毫秒）
     */
    public static void forceCleanTestDirectory(Path testDir, int maxRetries, long retryDelayMs) {
        if (testDir == null || !Files.exists(testDir)) {
            return;
        }

        log.info("开始强制清理测试目录: {}", testDir);

        // 首先强制垃圾回收
        forceGcAndWait(200);

        for (int i = 0; i < maxRetries; i++) {
            try {
                if (deleteDirectoryRecursively(testDir.toFile())) {
                    log.info("成功清理测试目录: {}", testDir);
                    return;
                }
            } catch (Exception e) {
                log.warn("清理测试目录失败，重试 {}/{}: {}", i + 1, maxRetries, e.getMessage());
            }

            if (i < maxRetries - 1) {
                try {
                    Thread.sleep(retryDelayMs);
                    // 每次重试前再次强制GC
                    forceGcAndWait(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }

        log.warn("无法完全清理测试目录: {}", testDir);
    }

    /**
     * 强制清理测试目录（使用默认参数）
     * 
     * @param testDir 测试目录路径
     */
    public static void forceCleanTestDirectory(Path testDir) {
        forceCleanTestDirectory(testDir, 10, 500);
    }

    /**
     * 递归删除目录
     * 
     * @param dir 要删除的目录
     * @return 是否删除成功
     */
    private static boolean deleteDirectoryRecursively(File dir) {
        if (dir == null || !dir.exists()) {
            return true;
        }

        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    if (!deleteDirectoryRecursively(file)) {
                        return false;
                    }
                } else {
                    if (!file.delete()) {
                        log.warn("无法删除文件: {}", file.getAbsolutePath());
                        return false;
                    }
                }
            }
        }

        return dir.delete();
    }

    /**
     * 强制垃圾回收并等待
     * 
     * @param maxWaitTime 最大等待时间（毫秒）
     */
    public static void forceGcAndWait(long maxWaitTime) {
        try {
            // 强制垃圾回收
            System.gc();
            
            // 等待一段时间让GC完成
            long startTime = System.currentTimeMillis();
            while (System.currentTimeMillis() - startTime < maxWaitTime) {
                Thread.sleep(10);
                // 再次触发GC
                System.gc();
            }
            
            log.debug("强制GC完成");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.warn("强制GC过程中被中断", e);
        } catch (Exception e) {
            log.warn("强制GC过程中出现错误", e);
        }
    }

    /**
     * 等待文件句柄释放
     * 
     * @param file 要等待的文件
     * @param timeoutMs 超时时间（毫秒）
     * @return 是否成功等待
     */
    public static boolean waitForFileRelease(File file, long timeoutMs) {
        if (file == null || !file.exists()) {
            return true;
        }

        long startTime = System.currentTimeMillis();
        while (System.currentTimeMillis() - startTime < timeoutMs) {
            try {
                // 尝试重命名文件，如果成功说明文件句柄已释放
                File tempFile = new File(file.getParentFile(), file.getName() + ".tmp");
                if (file.renameTo(tempFile)) {
                    tempFile.renameTo(file);
                    return true;
                }
            } catch (Exception e) {
                // 忽略异常，继续等待
            }

            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }

        return false;
    }
} 